import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:trollee/app/core/core.dart';

class CustomBubble extends StatelessWidget {
  const CustomBubble({
    required this.child,
    Key? key,
    this.arrowPosition = ArrowPosition.bottomCenter,
    this.color = AppColors.primary_color1,
    this.cornerRadius,
    this.angleMargin,
    this.angleTopMargin,
    this.padding,
    this.boxShadow,
  }) : super(key: key);

  final ArrowPosition arrowPosition;
  final Widget child;
  final Color color;

  final EdgeInsets? padding;

  /// 圆角，默认 10.w
  final double? cornerRadius;

  /// 三角形的边距，默认 12.w
  final double? angleMargin;

  /// 三角形的上边距，默认2.w
  final double? angleTopMargin;

  final List<BoxShadow>? boxShadow;

  @override
  Widget build(BuildContext context) {
    final clipper = BubbleClipper(
      arrowPosition: arrowPosition,
      cornerRadius: cornerRadius ?? AppDimens.dp10,
      angleMargin: angleMargin ?? AppDimens.dp12,
      angleTopMargin: angleTopMargin ?? AppDimens.dp2,
    );

    final widget = ClipPath(
      clipper: clipper,
      child: Container(
        padding: EdgeInsets.only(
          left: (padding?.left ?? AppDimens.dp16) + clipper.leftMargin,
          right: padding?.right ?? AppDimens.dp16,
          top: (padding?.top ?? AppDimens.dp8) + clipper.topMargin,
          bottom: (padding?.bottom ?? AppDimens.dp8) + clipper.bottomMargin,
        ),
        color: color,
        child: child,
      ),
    );

    return Material(
      color: Colors.transparent,
      child: boxShadow != null
          ? Container(
              decoration: BoxDecoration(boxShadow: boxShadow),
              child: widget,
            )
          : widget,
    );
  }
}

class BubbleClipper extends CustomClipper<Path> {
  BubbleClipper({
    this.arrowPosition = ArrowPosition.bottomCenter,
    this.cornerRadius = 10,
    this.angleMargin,
    this.angleTopMargin,
  }) {
    leftMargin = arrowPosition == ArrowPosition.leftCenter || arrowPosition == ArrowPosition.leftTop || arrowPosition == ArrowPosition.leftBottom
        ? AppDimens.dp12
        : AppDimens.dp0;

    bottomMargin = arrowPosition == ArrowPosition.bottomCenter || arrowPosition == ArrowPosition.bottomLeft || arrowPosition == ArrowPosition.bottomRight
        ? AppDimens.dp12
        : AppDimens.dp0;

    topMargin = arrowPosition == ArrowPosition.topCenter || arrowPosition == ArrowPosition.topLeft || arrowPosition == ArrowPosition.topRight
        ? AppDimens.dp12
        : AppDimens.dp0;
  }

  final ArrowPosition arrowPosition;

  late final double leftMargin;
  late final double bottomMargin;
  late final double topMargin;

  final arrowWidth = AppDimens.dp16;
  final arrowHeight = AppDimens.dp10;

  final double cornerRadius;

  /// 三角形的边距，默认是 12.w
  final double? angleMargin;

  /// 三角形的上边距，默认是2.w
  final double? angleTopMargin;

  @override
  Path getClip(Size size) {
    final width = size.width;
    final height = size.height;

    final path = Path()
      ..addRRect(RRect.fromRectAndRadius(
          Rect.fromLTWH(
            leftMargin,
            topMargin,
            width - leftMargin,
            height - bottomMargin - topMargin,
          ),
          Radius.circular(cornerRadius)));

    if (arrowPosition == ArrowPosition.bottomCenter || arrowPosition == ArrowPosition.bottomLeft || arrowPosition == ArrowPosition.bottomRight) {
      bottom(width, height, path);
    }

    if (arrowPosition == ArrowPosition.leftCenter || arrowPosition == ArrowPosition.leftTop || arrowPosition == ArrowPosition.leftBottom) {
      left(width, height, path);
    }

    if (arrowPosition == ArrowPosition.topCenter || arrowPosition == ArrowPosition.topLeft || arrowPosition == ArrowPosition.topRight) {
      top(width, height, path);
    }

    path.close();
    return path;
  }

  void bottom(width, height, path) {
    final arrowY = height - bottomMargin + angleTopMargin;
    var arrowX = (width - arrowWidth) / 2;
    if (arrowPosition == ArrowPosition.bottomLeft) {
      arrowX = angleMargin ?? 10.w;
    } else if (arrowPosition == ArrowPosition.bottomRight) {
      arrowX = width - arrowWidth - angleMargin ?? 10.w;
    }
    path.moveTo(arrowX + 1.5.w, arrowY);
    path.lineTo(arrowX + 1.5.w + 13.w, arrowY);
    path.cubicTo(
      arrowX + 1.5.w + 13.w + 0.67.w,
      arrowY,
      arrowX + arrowWidth,
      arrowY + 0.56.w,
      arrowX + arrowWidth,
      arrowY + 1.25.w,
    );
    path.cubicTo(
      arrowX + arrowWidth,
      arrowY + 1.5,
      arrowX + arrowWidth - 0.07.w,
      arrowY + 0.67.w,
      arrowX + 15.5.w,
      arrowY + 2.w,
    );
    path.lineTo(arrowX + (16.w - 2.5.w) / 2 + 2.5.w, arrowY + 9.5.w);
    path.quadraticBezierTo(arrowX + 8.w, arrowY + 11.w, arrowX + (16.w - 2.5.w) / 2, arrowY + 9.5.w);
    path.lineTo(arrowX + 0.5.w, arrowY + 2.w);
    path.cubicTo(
      arrowX + 0.07.w,
      arrowY + 1.73.w,
      arrowX,
      arrowY + 1.5.w,
      arrowX,
      arrowY + 1.25.w,
    );
    path.cubicTo(
      arrowX,
      arrowY + 0.56.w,
      arrowX + 0.07.w,
      arrowY,
      arrowX + 1.5.w,
      arrowY,
    );
  }

  void left(width, height, path) {
    var arrowY = height / 2 - arrowWidth / 2;
    final arrowX = arrowHeight;

    if (arrowPosition == ArrowPosition.leftTop) {
      arrowY = arrowWidth;
    } else if (arrowPosition == ArrowPosition.leftBottom) {
      arrowY = height - arrowWidth * 2;
    }

    path.moveTo(arrowX, arrowY + 1.5.w);
    path.lineTo(arrowX, arrowY + arrowWidth - 1.5.w);

    path.cubicTo(
      arrowX,
      arrowY + arrowWidth - 0.67.w,
      arrowX - 0.56.w,
      arrowY + arrowWidth,
      arrowX - 1.25.w,
      arrowY + arrowWidth,
    );

    path.cubicTo(
      arrowX - 1.5,
      arrowY + arrowWidth,
      arrowX - 0.67.w,
      arrowY + arrowWidth - 0.07.w,
      arrowX - 2.w,
      arrowY + arrowWidth - 0.5.w,
    );

    path.lineTo(
      arrowX - 9.5.w,
      arrowY + (16.w - 2.5.w) / 2 + 2.5.w,
    );
    path.quadraticBezierTo(
      arrowX - 11.w,
      arrowY + 8.w,
      arrowX - 9.5.w,
      arrowY + (16.w - 2.5.w) / 2,
    );
    path.lineTo(
      arrowX - 2.w,
      arrowY + 0.5.w,
    );
    path.cubicTo(
      arrowX - 1.73.w,
      arrowY + 0.07.w,
      arrowX - 1.5.w,
      arrowY,
      arrowX - 1.25.w,
      arrowY,
    );
    path.cubicTo(
      arrowX - 0.56.w,
      arrowY,
      arrowX,
      arrowY + 0.07.w,
      arrowX,
      arrowY + 1.5.w,
    );
  }

  void top(width, height, path) {
    final arrowY = arrowHeight;
    var arrowX = (width - arrowWidth) / 2;
    if (arrowPosition == ArrowPosition.topLeft) {
      arrowX = angleMargin ?? 10.w;
    } else if (arrowPosition == ArrowPosition.topRight) {
      arrowX = width - arrowWidth - angleMargin ?? 10.w;
    }
    path.moveTo(arrowX + 1.5.w, arrowY);
    path.lineTo(arrowX + 1.5.w + 13.w, arrowY);
    path.cubicTo(
      arrowX + 1.5.w + 13.w + 0.67.w,
      arrowY,
      arrowX + arrowWidth,
      arrowY - 0.56.w,
      arrowX + arrowWidth,
      arrowY - 1.25.w,
    );
    path.cubicTo(
      arrowX + arrowWidth,
      arrowY - 1.5,
      arrowX + arrowWidth - 0.07.w,
      arrowY - 0.67.w,
      arrowX + 15.5.w,
      arrowY - 2.w,
    );
    path.lineTo(arrowX + (16.w - 2.5.w) / 2 + 2.5.w, arrowY - 9.5.w);
    path.quadraticBezierTo(arrowX + 8.w, arrowY - 11.w, arrowX + (16.w - 2.5.w) / 2, arrowY - 9.5.w);
    path.lineTo(arrowX + 0.5.w, arrowY - 2.w);
    path.cubicTo(
      arrowX + 0.07.w,
      arrowY - 1.73.w,
      arrowX,
      arrowY - 1.5.w,
      arrowX,
      arrowY - 1.25.w,
    );
    path.cubicTo(
      arrowX,
      arrowY - 0.56.w,
      arrowX + 0.07.w,
      arrowY,
      arrowX + 1.5.w,
      arrowY,
    );
  }

  @override
  bool operator ==(dynamic other) {
    if (other.runtimeType != runtimeType) {
      return false;
    }
    if (identical(this, other)) {
      return true;
    }
    return arrowPosition == other.arrowPosition;
  }

  @override
  bool shouldReclip(covariant CustomClipper<Path> oldClipper) {
    return oldClipper != this;
  }

  @override
  int get hashCode => arrowPosition.hashCode ^ cornerRadius.hashCode ^ angleMargin.hashCode ^ angleTopMargin.hashCode;
}

enum ArrowPosition {
  bottomCenter,
  bottomLeft,
  bottomRight,
  leftCenter,
  leftTop,
  leftBottom,
  topCenter,
  topLeft,
  topRight,
}
